/* 
 * File:   Agent.cpp
 * Author: flai0014
 * 
 * Created on March 30, 2012, 11:49 AM
 */

#include "Agent.h"

Agent::Agent(int agentNum): Thread() {
    //m_enviroment = workingEnviroment;
    location = 0;
    m_agentID = agentNum;
    stopFlag = false;
    hasParticle = false;
}

//Agent::Agent(const Agent& orig) : Thread(orig){
//}

Agent::~Agent() {
    
}

void* Agent::run(void* attb){
    m_agent = static_cast<Agent*> (attb);
    pthread_mutex_lock(&Enviroment::m_mutex);
    cerr << "Agent " << m_agentID << " waiting to start...\n";//Initial wait
    pthread_cond_wait(&Enviroment::wait_condition_var, &Enviroment::m_mutex);
    cerr << "Agent " << m_agentID << " starting...\n";
    pthread_mutex_unlock(&Enviroment::m_mutex);
    while(!stopFlag){
        if(Enviroment::countWait()){//Checks for a count wait flag
            dropItem();
            checkIn();            
        }else if(Enviroment::printWait()){// Checks for a print_wait flag
            checkIn();
        }
        move();//does a move first
        checkForParticile();//Check for a particle
    }
    pthread_mutex_lock(&Enviroment::m_mutex);
    Enviroment::agentCheckIn();
    pthread_cond_signal(&Enviroment::count_condition_var);
    cerr << "Agent " << m_agentID << " stopping...\n";
    pthread_mutex_unlock(&Enviroment::m_mutex);
}

void Agent::stop(){
    dropItem();
    stopFlag = true;
}

void Agent::checkIn(){
    pthread_mutex_lock(&Enviroment::m_mutex);
    Enviroment::agentCheckIn();
    pthread_cond_signal(&Enviroment::count_condition_var);
    cerr << "Agent " << m_agentID << " waiting...\n";
    while(Enviroment::countWait() || Enviroment::printWait()){
        pthread_cond_wait(&Enviroment::wait_condition_var, 
                &Enviroment::m_mutex);//Waits for the enviroment to get done
    }
    cerr << "Agent " << m_agentID << " starting...\n";
    //Enviroment::agentCheckIn();
    //pthread_cond_signal(&Enviroment::count_condition_var);
    pthread_mutex_unlock(&Enviroment::m_mutex);    
}

void Agent::checkForParticile(){
    pthread_mutex_lock(&Enviroment::m_mutex);
    if(rand()%10 >= PROB){//Number 0-9 and is true on 0, 1, 2 or 30%
        pthread_mutex_unlock(&Enviroment::m_mutex);
        return; //Return on if probability if not true
    }
//    if(hasParticle)
//        cerr << "Droping particle: Agent ID: " << m_agentID << endl;
//    else
//        cerr << "Picking up item: Agent ID: " << m_agentID << endl;
    pthread_mutex_unlock(&Enviroment::m_mutex);
    if(hasParticle){        
        dropItem();//If you have an item then drop it
    }else{
        pickUpItem();//Other wise pickup a particle
    }
}

void Agent::startPickup(){
    pthread_mutex_lock(&Enviroment::m_mutex);
    m_cell = Enviroment::getCell(location);
    m_cell->agentCheckIn();
    while(m_cell->checkLock())
        pthread_cond_wait(&condition_var, &Enviroment::m_mutex);
    m_cell->getLock();//Obtains the lock for this thread
    pthread_mutex_unlock(&Enviroment::m_mutex);    
}

void Agent::endPickup(){
    pthread_mutex_lock(&Enviroment::m_mutex);
    m_cell->agentCheckOut();
    m_cell->releaseLock();//Releases the lock
    pthread_cond_signal(&condition_var);
    m_cell = NULL;
    pthread_mutex_unlock(&Enviroment::m_mutex);
    
}

void Agent::startDrop(){
    pthread_mutex_lock(&Enviroment::m_mutex);
    m_cell = Enviroment::getCell(location);
    m_cell->agentCheckIn();
    while(m_cell->checkLock())
        pthread_cond_wait(&condition_var, &Enviroment::m_mutex);
    m_cell->getLock();
    pthread_mutex_unlock(&Enviroment::m_mutex); 
}

void Agent::endDrop(){
    pthread_mutex_lock(&Enviroment::m_mutex);
    m_cell->agentCheckOut();
    m_cell->releaseLock();
    pthread_cond_signal(&condition_var);
    m_cell = NULL;
    pthread_mutex_unlock(&Enviroment::m_mutex);    
}

void Agent::move(){
    int nxtDirection;
    pthread_mutex_lock(&Enviroment::m_mutex);
    nxtDirection = rand()%DIRECTIONS;
    location = Enviroment::move(nxtDirection, location);
    pthread_mutex_unlock(&Enviroment::m_mutex);
    
}

void Agent::dropItem(){
    if(!hasParticle)
        return;
    startDrop();
    m_cell->addParticle(m_particle);
    m_particle = NULL;
    hasParticle = false;
    endDrop();     
}

void Agent::pickUpItem(){
    startPickup();
    if(m_cell->numberOfParticles() > 0){
            m_particle = m_cell->removeParticle();
            hasParticle = true;
    }
    endPickup();    
}

pthread_cond_t Agent::condition_var =  PTHREAD_COND_INITIALIZER;
